#include <p24FJ64GA102.h>
#include "TCPIP Stack/TCPIP.h"

#include "StringUtils.h"
#include "Protocol.h"

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#ifndef NULL
#define NULL 0
#endif

#define TCP_LISTENER_PORT 4096

static int tcpSocket = INVALID_SOCKET;
static int isInitialized = FALSE;

extern Protocol GetLedsProtocol, SetLedsProtocol, GetSwitchesProtocol;
//extern Protocol GetLedsProtocol, SetLedsProtocol, GetSwitchesProtocol, InvalidProtocol;
extern volatile int isWiFiConnected;

static void tcpInitialize(void);
static void tcpHandleConnection(void);
static void processRequest(BYTE* rxBuffer, WORD rxLength, BYTE* txBuffer, WORD* txBytesToSend);

void (*tcpReceptionCallback)(BYTE* rxBuffer, WORD rxLength, BYTE* txBuffer, WORD* txBytesToSend) = processRequest;

void tcpListener(void) {
	if (isWiFiConnected) {
		if (!isInitialized) {
			tcpInitialize();
			return; //allow StackTask() to be called in outside loop
		}

/*
		//check for socket disconnections in-between calls
		if (tcpSocket != INVALID_SOCKET && TCPWasReset(tcpSocket)) {
			TCPClose(tcpSocket);
			tcpSocket = INVALID_SOCKET;
		}
*/
		//restart if disconnected/unable to connect
		if (tcpSocket == INVALID_SOCKET) {
			isInitialized = FALSE;
			return;
		}
	
		//restart if not yet connected
		if (!TCPIsConnected(tcpSocket))
			return;
	
		//handle connection if connected
		tcpHandleConnection();
	}
	else {
		if (isInitialized) {
			//Loss of connection: remove sockets
			isInitialized = FALSE;
			if (tcpSocket != INVALID_SOCKET) {
				TCPClose(tcpSocket);
				tcpSocket = INVALID_SOCKET;
			}
		}
	}
}

static void tcpInitialize(void) {
	tcpSocket = TCPOpen(0, TCP_OPEN_SERVER, TCP_LISTENER_PORT, TCP_PURPOSE_DEFAULT);
	if (tcpSocket != INVALID_SOCKET)
		isInitialized = TRUE;
}

static void tcpHandleConnection(void) {
	WORD rxBytesAvailable;

	rxBytesAvailable = TCPIsGetReady(tcpSocket);

	if (rxBytesAvailable > 0) {
		BYTE rxBuffer[32];
		WORD rxBytesToRead;
		WORD rxBytesRead;
		BYTE txBuffer[32];
		WORD txBytesToSend;

		rxBytesToRead = rxBytesAvailable < sizeof(rxBuffer) ? rxBytesAvailable : sizeof(rxBuffer);
		rxBytesRead = TCPGetArray(tcpSocket, rxBuffer, rxBytesToRead);

		processRequest(rxBuffer, rxBytesToRead, txBuffer, &txBytesToSend);
		if (txBytesToSend > 0) {
			TCPPutArray(tcpSocket, txBuffer, txBytesToSend);
		}
		TCPClose(tcpSocket);
		tcpSocket = INVALID_SOCKET;
	}
}

#ifdef _TEST

static void processRequest(BYTE* rxBuffer, WORD rxLength, BYTE* txBuffer, WORD* txBytesToSend) {
	intToString((int)rxLength, (char*)txBuffer);
	strcat((char*)txBuffer, "ACK");

	*txBytesToSend = strlen((char*)txBuffer);
}

#else
static void processRequest(BYTE* rxBuffer, WORD rxLength, BYTE* txBuffer, WORD* txBytesToSend) {
	if (GetLedsProtocol.matchesRequest(rxBuffer, rxLength))
		GetLedsProtocol.buildResponse(rxBuffer, rxLength, txBuffer, txBytesToSend);
	else if (SetLedsProtocol.matchesRequest(rxBuffer, rxLength))
		SetLedsProtocol.buildResponse(rxBuffer, rxLength, txBuffer, txBytesToSend);
	else if (GetSwitchesProtocol.matchesRequest(rxBuffer, rxLength))
		GetSwitchesProtocol.buildResponse(rxBuffer, rxLength, txBuffer, txBytesToSend);
	//else
		//InvalidProtocol.buildResponse(rxBuffer, rxLength, txBuffer, txBytesToSend);
}
#endif
